/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.document;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationModule;
import cn.ac.nercis.pes.common.constant.evaluation.InfoLevel;
import cn.ac.nercis.pes.common.constant.knowledge.VirtualClassification;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.common.constant.project.SystemState;
import cn.ac.nercis.pes.model.bo.doc.report.stat.TargetStatBO;
import cn.ac.nercis.pes.model.dto.evaluation.analyze.IntegrityVerificationDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemProcessRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemProcessDO;
import cn.ac.nercis.pes.service.document.report.ReportOverallEvaluationDataService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 完整性验证服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record IntegrityVerificationService(SystemProcessRepository systemProcessRepository,
                                           EvaluationRecordRepository evaluationRecordRepository,
                                           AssetRepository assetRepository,
                                           AssetEvaluationMethodRepository evaluationMethodRepository,
                                           ReportOverallEvaluationDataService overallEvaluationDataService,
                                           SystemRepository systemRepository) {
    @Autowired
    public IntegrityVerificationService {
    }

    public List<IntegrityVerificationDTO> checkIntegrity(String systemId) {
        List<SystemProcessDO> systemProcess = systemProcessRepository.findAllBySystemId(systemId);
        List<IntegrityVerificationDTO> result = checkPrepareData(systemProcess);
        result.addAll(checkPlanningData(systemProcess));
        result.addAll(checkEvaluationData(systemId));
        result.addAll(checkReportData(systemProcess));
        return result;
    }

    public List<IntegrityVerificationDTO> checkIntegrityByReport(String systemId){
        var result = new ArrayList<>(checkTargetCount(systemId));
        result.addAll(checkIntegrity(systemId));
        return result;
    }

    public List<IntegrityVerificationDTO> checkTargetCount(String systemId) {
        SystemDO systemDO = systemRepository.findById(systemId).orElse(null);
        if(Objects.isNull(systemDO)){
            return Collections.emptyList();
        }
        var targetStat = overallEvaluationDataService.getTargetStat(systemDO);
        return Stream.of(checkInapplicableCount(targetStat,systemDO),
                        checkAccordCount(targetStat),
                        checkPartAccordCount(targetStat),
                        checkNotAccordCount(targetStat))
                .filter(Objects::nonNull)
                .toList();
    }

    private IntegrityVerificationDTO checkInapplicableCount(TargetStatBO targetStat, SystemDO systemDO){
        var count = targetStat.getInapplicableCount();
        var sumCount = targetStat.getPhyEnv().getInapplicableCount()
                + targetStat.getNetCommunication().getInapplicableCount()
                + targetStat.getDeviceCompute().getInapplicableCount()
                + targetStat.getAppData().getInapplicableCount()
                + targetStat.getManageSystem().getInapplicableCount()
                + targetStat.getPersonnelManage().getInapplicableCount()
                + targetStat.getBuildRunning().getInapplicableCount()
                + targetStat.getEmergencyHandling().getInapplicableCount();
        if(count != sumCount){
            var statRecord = overallEvaluationDataService.getInapplicableTargetStatRecord(systemDO);
            return IntegrityVerificationDTO.builder()
                    .name("测评指标确认中不适用项数与现场测评中的不一致")
                    .level(InfoLevel.WARN)
                    //.detail(statRecord)
                    .build();
        }else{
            return null;
        }
    }

    private IntegrityVerificationDTO checkAccordCount(TargetStatBO targetStat){
        var count = targetStat.getAccordCount();
        var sumCount = targetStat.getPhyEnv().getAccordCount()
                + targetStat.getNetCommunication().getAccordCount()
                + targetStat.getDeviceCompute().getAccordCount()
                + targetStat.getAppData().getAccordCount()
                + targetStat.getManageSystem().getAccordCount()
                + targetStat.getPersonnelManage().getAccordCount()
                + targetStat.getBuildRunning().getAccordCount()
                + targetStat.getEmergencyHandling().getAccordCount();
        if(count != sumCount){
            return IntegrityVerificationDTO.builder()
                    .name("检测到符合项数与实际不一致")
                    .level(InfoLevel.ERROR)
                    .build();
        }else{
            return null;
        }
    }

    private IntegrityVerificationDTO checkPartAccordCount(TargetStatBO targetStat){
        var count = targetStat.getPartAccordCount();
        var sumCount = targetStat.getPhyEnv().getPartAccordCount()
                + targetStat.getNetCommunication().getPartAccordCount()
                + targetStat.getDeviceCompute().getPartAccordCount()
                + targetStat.getAppData().getPartAccordCount()
                + targetStat.getManageSystem().getPartAccordCount()
                + targetStat.getPersonnelManage().getPartAccordCount()
                + targetStat.getBuildRunning().getPartAccordCount()
                + targetStat.getEmergencyHandling().getPartAccordCount();
        if(count != sumCount){
            return IntegrityVerificationDTO.builder()
                    .name("检测到部分符合项数与实际不一致")
                    .level(InfoLevel.ERROR)
                    .build();
        }else{
            return null;
        }
    }

    private IntegrityVerificationDTO checkNotAccordCount(TargetStatBO targetStat){
        var count = targetStat.getNotAccordCount();
        var sumCount = targetStat.getPhyEnv().getNotAccordCount()
                + targetStat.getNetCommunication().getNotAccordCount()
                + targetStat.getDeviceCompute().getNotAccordCount()
                + targetStat.getAppData().getNotAccordCount()
                + targetStat.getManageSystem().getNotAccordCount()
                + targetStat.getPersonnelManage().getNotAccordCount()
                + targetStat.getBuildRunning().getNotAccordCount()
                + targetStat.getEmergencyHandling().getNotAccordCount();
        if(count != sumCount){
            return IntegrityVerificationDTO.builder()
                    .name("检测到不符合项数与实际不一致")
                    .level(InfoLevel.ERROR)
                    .build();
        }else{
            return null;
        }
    }

    private List<IntegrityVerificationDTO> checkPrepareData(List<SystemProcessDO> systemProcess) {
        List<SystemProcessDO> process = findProcessByState(systemProcess, SystemState.PREPARE);
        List<IntegrityVerificationDTO> verifications = new ArrayList<>();
        for (EvaluationModule module : EvaluationModule.findPrepareModules()) {
            if (module == EvaluationModule.SYSTEM_ASSET) {
                verifications.addAll(checkPrepareSystemAssetData(process));
            } else {
                verifications.add(getIntegrityVerification(process, String.format("[%s]", SystemState.PREPARE.getName()), module.getName()));
            }
        }
        return verifications;
    }

    private List<IntegrityVerificationDTO> checkPrepareSystemAssetData(List<SystemProcessDO> process) {
        List<IntegrityVerificationDTO> result = EvaluationModule.findSystemAssetModules()
                .stream()
                .map(m -> getIntegrityVerification(process, String.format("[%s]系统资产/", SystemState.PREPARE.getName()), m.getName()))
                .collect(Collectors.toList());
        long finishCount = result.stream().filter(iv -> iv.getLevel() == InfoLevel.INFO).count();
        if (finishCount == result.size()) {
            result.clear();
            result.add(IntegrityVerificationDTO.builder()
                    .name(String.format("[%s]系统资产", SystemState.PREPARE.getName()))
                    .level(InfoLevel.INFO)
                    .build());
        }
        return result;
    }

    private IntegrityVerificationDTO getIntegrityVerification(List<SystemProcessDO> process, String funName, String moduleName) {
        FinishState finishState = process.stream()
                .filter(sp -> StringUtils.equals(sp.getName(), moduleName))
                .map(SystemProcessDO::getFinishState)
                .findFirst()
                .orElse(FinishState.NONE);
        return IntegrityVerificationDTO.builder()
                .name(funName.concat(moduleName))
                .level(finishState == FinishState.FINISH ? InfoLevel.INFO : InfoLevel.WARN)
                .build();
    }

    private List<IntegrityVerificationDTO> checkPlanningData(List<SystemProcessDO> systemProcess) {
        List<SystemProcessDO> process = findProcessByState(systemProcess, SystemState.PLANNING);
        List<IntegrityVerificationDTO> result = checkPlanningData(EvaluationModule.findTargetConfirmModules(), process, "测评指标确认");
        result.addAll(checkPlanningData(EvaluationModule.findContentConfirmModules(), process, "测评内容确认"));
        result.addAll(checkPlanningData(EvaluationModule.findEvaRequireModules(), process, "测评配合需求"));
        return result;
    }

    private List<IntegrityVerificationDTO> checkPlanningData(List<EvaluationModule> modules, List<SystemProcessDO> process, String moduleName) {
        List<IntegrityVerificationDTO> result = modules.stream()
                .map(m -> getIntegrityVerification(process, String.format("[%s]", SystemState.PLANNING.getName()), m.getName()))
                .collect(Collectors.toList());
        long finishCount = result.stream().filter(iv -> iv.getLevel() == InfoLevel.INFO).count();
        if (finishCount == result.size()) {
            result.clear();
            result.add(IntegrityVerificationDTO.builder()
                    .name(String.format("[%s]%s", SystemState.PLANNING.getName(), moduleName))
                    .level(InfoLevel.INFO)
                    .build());
        }
        return result;
    }


    private List<IntegrityVerificationDTO> checkEvaluationData(String systemId) {
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemId(systemId);
        List<AssetEvaluationMethodDO> evaluationMethods = evaluationMethodRepository.findAllBySystemId(systemId);
        List<AssetDO> assets = assetRepository.findAllBySystemId(systemId);
        return Arrays.stream(VirtualClassification.values())
                .map(ac -> IntegrityVerificationDTO.builder()
                        .name(String.format("[%s]%s", SystemState.EVALUATION.getName(), ac.getName()))
                        .level(checkEvaluationData(ac.getClassIds(), assets, evaluationMethods, evaluationRecords))
                        .build())
                .collect(Collectors.toList());
    }

    private InfoLevel checkEvaluationData(List<String> assetClassIds, List<AssetDO> assets,
                                          List<AssetEvaluationMethodDO> evaluationMethods,
                                          List<EvaluationRecordDO> evaluationRecords) {
        List<FinishState> result = assets.stream()
                .filter(a -> assetClassIds.stream().anyMatch(classId -> StringUtils.equals(a.getClassId(), classId)))
                .filter(AssetDO::getIsUse)
                .map(a -> checkEvaluationData(a, evaluationMethods, evaluationRecords))
                .toList();
        long finishCount = result.stream().filter(r -> r == FinishState.FINISH).count();
        if (finishCount == result.size()) {
            return InfoLevel.INFO;
        } else {
            return InfoLevel.WARN;
        }

    }

    private FinishState checkEvaluationData(AssetDO assetDO, List<AssetEvaluationMethodDO> evaluationMethods,
                                            List<EvaluationRecordDO> evaluationRecords) {
        long count = evaluationMethods.stream()
                .filter(em -> StringUtils.equals(em.getAssetId(), assetDO.getId()))
                .count();
        if (count < 1) {
            return FinishState.NONE;
        }
        long finishCount = evaluationRecords.stream()
                .filter(er -> StringUtils.equals(er.getAsset().getId(), assetDO.getId())
                        && er.getFinishState() == FinishState.FINISH)
                .count();
        return finishCount == count ? FinishState.FINISH : FinishState.INCOMPLETE;
    }

    private List<IntegrityVerificationDTO> checkReportData(List<SystemProcessDO> systemProcess) {
        List<SystemProcessDO> process = findProcessByState(systemProcess, SystemState.REPORT);
        List<IntegrityVerificationDTO> result = checkReportData(EvaluationModule.findUnitEvalModules(), process, "单元测评");
        result.addAll(checkReportData(EvaluationModule.findOverallEvalModules(), process, "整体测评"));
        result.addAll(checkReportData(EvaluationModule.findRiskAnalysisEvalModules(), process, "风险分析"));
        result.addAll(checkReportData(EvaluationModule.findOverallAppraiseEvalModules(), process, "总体评价"));
        return result;
    }

    private List<IntegrityVerificationDTO> checkReportData(List<EvaluationModule> modules, List<SystemProcessDO> process, String moduleName) {
        List<IntegrityVerificationDTO> result = modules.stream()
                .map(m -> getIntegrityVerification(process, String.format("[%s]", SystemState.REPORT.getName()), m.getName()))
                .collect(Collectors.toList());
        long finishCount = result.stream().filter(iv -> iv.getLevel() == InfoLevel.INFO).count();
        if (finishCount == result.size()) {
            result.clear();
            result.add(IntegrityVerificationDTO.builder()
                    .name(String.format("[%s]%s", SystemState.REPORT.getName(), moduleName))
                    .level(InfoLevel.INFO)
                    .build());
        }
        return result;
    }

    private List<SystemProcessDO> findProcessByState(List<SystemProcessDO> systemProcess, SystemState state) {
        return systemProcess.stream()
                .filter(sp -> sp.getState() == state)
                .collect(Collectors.toList());
    }
}
